package fctuc.dei.myTwitter.com;

import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;

/**
 * @author <a:mailto="becarv@dei.uc.pt" />Bruno de Carvalho</a>
 */
public class CumulativeReader {

    // internal vars
    private int objectSize;
    private ByteBuffer serializedObject;
    private boolean clean;

    // constructors ---------------------------------------------------------------------------------------------------

    public CumulativeReader() {
        this.clean = true;
    }

    // public methods -------------------------------------------------------------------------------------------------

    /**
     * Prepare this reader for a new object.
     */
    public void reset() {
        this.clean = true;
        this.serializedObject = null;
        this.objectSize = 0;
    }

    /**
     * Append a buffer into the existing partial serializedObject.
     *
     * @param data Buffer containing more data.
     */
    public void appendData(ByteBuffer data) {
        if (this.serializedObject == null) {
            if (this.objectSize <= 0) {
                throw new IllegalStateException("ObjectSize must be set prior to appending data...");
            } else {
                this.serializedObject = ByteBuffer.allocate(this.objectSize);
            }
        }

        this.clean = false;
        // If the amount of bytes in the input buffer (data) is less or equal to the missing bytes for the object
        // then copy everything into the serializedObject buffer.
        // If the input buffer contains more bytes than needed to finish the object, then copy only the necessary bytes.
        int numBytesToCopy = Math.min(data.remaining(), this.getMissingBytes());
//        System.out.println("[CumulativeReader] Appended " + numBytesToCopy + " bytes to serializedObject buffer.");
//        if (numBytesToCopy < data.remaining()) {
//            System.out.println("[CumulativeReader] Source buffer contains more data than needed to finish object," +
//                               " discarding " + (data.remaining() - numBytesToCopy) + " bytes");
//        }
        this.serializedObject.put(data.array(), data.position(), numBytesToCopy);
        // advance the buffer's position to match the portion of data copied (needs to be done manually as ByteBuffer
        // provides no way to copy a portion of data from a ByteBuffer into another ByteBuffer, so what we need to do
        // is access the backing array directly, copy whatever we need and then manually set the new position on the
        // wrapping buffer).
        data.position(data.position() + numBytesToCopy);
    }

    /**
     * Mark reading as finished - flip and return buffer.
     *
     * @return Flipped buffer.
     */
    public ByteBuffer terminateData() {
        this.serializedObject.flip();
        return this.serializedObject;
    }

    public int getMissingBytes() {
        if (this.serializedObject == null) {
            return -1;
        }

        return this.objectSize - this.serializedObject.position();
    }

    /**
     * Tests whether the object is complete or not.
     *
     * @return True if the object is complete, false otherwise.
     */
    public boolean isObjectComplete() {
        return (this.serializedObject != null) &&
               (this.objectSize != 0) &&
               (this.serializedObject.position() == this.objectSize);
    }

    // getters & setters ----------------------------------------------------------------------------------------------

    public int getObjectSize() {
        return objectSize;
    }

    public void setObjectSize(int objectSize) {
        this.clean = false;
        this.objectSize = objectSize;
        this.serializedObject = ByteBuffer.allocate(objectSize);
    }

    public ByteBuffer getSerializedObject() {
        return serializedObject;
    }

    public boolean isClean() {
        return clean;
    }
}
